import { SpriteFrame } from "cc";
import { isValid } from "cc";
import { TextAsset } from "cc";
import { AssetManager } from "cc";
import { Asset, Prefab, resources } from "cc";
import { BaseMgr } from "../base/BaseMgr";
import { sp } from "cc";
import { assetManager } from "cc";
import { PREVIEW } from "cc/env";
import { DynamicAtlasManager } from "cc";

//启动bundle后，需要关闭动态合图，不然bundle资源释放后，动态合进去的是无法被释放导致泄露
!PREVIEW && (DynamicAtlasManager.instance.enabled = false);

export type TProgress = (finished: number, total: number, item: AssetManager.RequestItem) => void

export default class ResMgr extends BaseMgr {
    static get inst() {
        return this.getInstance();
    }
    private cache: { [path: string]: Asset } = {};
    private loadSuccessQueue: { [path: string]: Function[] } = {};
    private loadFailedQueue: { [path: string]: Function[] } = {};

    loadPrefab(path: string, bundleName?: string) {
        return this.load(path, Prefab, bundleName);
    }

    loadSpine(path: string, bundleName?: string) {
        return this.load(`${path}`, sp.SkeletonData, bundleName);
    }

    loadSpriteFrame(path: string, bundleName?: string) {
        return this.load(`${path}/spriteFrame`, SpriteFrame, bundleName);
    }

    loadTxtAssets(path: string, bundleName?: string) {
        return this.load(path, TextAsset, bundleName);
    }

    load<T extends Asset>(
        path: string,
        type: new () => T,
        bundleName?: string,
        onProgress?: TProgress,
    ): Promise<T> {
        return new Promise(async (resolve, reject) => {
            let cachePath = path;
            if (bundleName) {
                cachePath = `${bundleName}_${cachePath}`;
            }
            const asset = this.cache[cachePath];
            if (asset && isValid(asset)) {
                resolve(asset as T);
            } else {
                if (!this.loadFailedQueue[cachePath]) {
                    this.loadFailedQueue[cachePath] = [];
                }
                this.loadFailedQueue[cachePath].push(reject);

                if (!this.loadSuccessQueue[cachePath]) {
                    this.loadSuccessQueue[cachePath] = [];
                }
                this.loadSuccessQueue[cachePath].push(resolve);
                if (this.loadSuccessQueue[cachePath].length == 1) {
                    let loader = resources;
                    if (bundleName) {
                        loader = await this.loadBundle(bundleName);
                    }
                    if (loader) {
                        const loadComplete = (e: Error, data: Asset) => {
                            if (e) {
                                while (this.loadFailedQueue[cachePath].length > 0) {
                                    const rej = this.loadFailedQueue[cachePath].shift();
                                    console.error(e);
                                    rej && rej(e);
                                }
                                return;
                            }
                            this.cache[cachePath] = data;
                            while (this.loadSuccessQueue[cachePath].length > 0) {
                                const res = this.loadSuccessQueue[cachePath].shift();
                                res && res(data as T);
                            }
                        }
                        if (!onProgress) {
                            loader.load(path, type, loadComplete);
                        } else {
                            let percent = 0;
                            loader.load(path, type, (finished, total, item) => {
                                const p = finished / total;
                                if (p > percent) {
                                    percent = p;
                                    onProgress(finished, total, item);
                                }
                            }, loadComplete);
                        }
                    }
                }
            }
        });
    }

    loadDir<T extends Asset>(path: string, onProgress: TProgress, bundleName?: string): Promise<T[]> {
        return new Promise(async (resolve, reject) => {
            let loader = resources;
            if (bundleName) {
                loader = await this.loadBundle(bundleName);
            }
            let percent = 0;
            loader.loadDir(path, (finished, total, item) => {
                const p = finished / total;
                if (p > percent) {
                    percent = p;
                    onProgress(finished, total, item);
                }
            }, (e, data) => {
                if (e) {
                    reject(e);
                    return;
                }
                resolve(data as T[]);
            });
        });
    }

    loadBundle(bundleName: string): Promise<AssetManager.Bundle> {
        return new Promise((resolve, reject) => {
            const cache = assetManager.getBundle(bundleName);
            if (cache && isValid(cache)) {
                resolve(cache)
            } else {
                console.log("加载bundle", bundleName)
                assetManager.loadBundle(bundleName, (err: Error, bundle) => {
                    if (err) {
                        return reject(err);
                    }
                    resolve(bundle);
                });
            }
        });
    }

    releaseBundle(bundleName: string) {
        if (PREVIEW) return;
        const bundle = assetManager.getBundle(bundleName);
        if (bundle) {
            bundle.releaseAll();
        }
    }
}